﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MediaBrowserWS.Service.Enums;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace MediaBrowserWS.Service.Classes
{
    /// <summary>
    /// A lot of this code is borrowed from
    /// http://VmcController.codeplex.com, so I can't take credit for this..
    /// </summary>
    public class SendKey
    {
        #region Private Members

        private static Dictionary<SpecialKeys, ushort> _specialKeysDicationary;
        private static Dictionary<SpecialKeys, ushort> SpecialKeysDictionary
        {
            get
            {
                if (_specialKeysDicationary == null)
                    InitialiseSpecialKeyDictionary();
                return _specialKeysDicationary;
            }
        }

        private ushort key;
        SpecialKeys specialKeys;

        #endregion

        private static void InitialiseSpecialKeyDictionary()
        {
            _specialKeysDicationary = new Dictionary<SpecialKeys, ushort>();

            _specialKeysDicationary.Add(SpecialKeys.Alt, 0x12);
            _specialKeysDicationary.Add(SpecialKeys.Control, 0x11);
            _specialKeysDicationary.Add(SpecialKeys.Shift, 0x10);
            _specialKeysDicationary.Add(SpecialKeys.Windows, 0x5b);
        }

        public SendKey(ushort key, SpecialKeys specialKeys)
        {
            this.key = key;
            this.specialKeys = specialKeys;
        }

        public SendKey(ushort key)
        {
            this.key = key;
            this.specialKeys = SpecialKeys.None;
        }

        public void Execute()
        {
            if (!NativeMethods.SetForegroundWindow(GetWindowHandle()))
            {
                throw new InvalidOperationException("Unable to find MediaCenter window.");
            }

            if (IntPtr.Size == 8)
            {
                // 64 bit machine
                Send64Command();
            }
            else if (IntPtr.Size == 4)
            {
                // 32 bit machine
                Send32Command();
            }
        }

        private void Send64Command()
        {
            NativeMethods.INPUT64 structInput;
            structInput = new NativeMethods.INPUT64();
            structInput.type = NativeMethods.INPUT_KEYBOARD;
            structInput.ki.wScan = 0;
            structInput.ki.time = 0;
            structInput.ki.dwFlags = 0;
            structInput.ki.dwExtraInfo = IntPtr.Zero;

            DoSpecial(SpecialKeys.Alt, ref structInput);
            DoSpecial(SpecialKeys.Control, ref structInput);
            DoSpecial(SpecialKeys.Shift, ref structInput);
            DoSpecial(SpecialKeys.Windows, ref structInput);

            DoKey(this.key, ref structInput);
            // Key up the actual key-code
            structInput.ki.dwFlags = NativeMethods.KEY_UP;
            DoKey(this.key, ref structInput);

            DoSpecial(SpecialKeys.Alt, ref structInput);
            DoSpecial(SpecialKeys.Control, ref structInput);
            DoSpecial(SpecialKeys.Shift, ref structInput);
            DoSpecial(SpecialKeys.Windows, ref structInput);
        }

        private void Send32Command()
        {
            NativeMethods.INPUT structInput;
            structInput = new NativeMethods.INPUT();
            structInput.type = NativeMethods.INPUT_KEYBOARD;
            structInput.ki.wScan = 0;
            structInput.ki.time = 0;
            structInput.ki.dwFlags = 0;
            structInput.ki.dwExtraInfo = IntPtr.Zero;

            DoSpecial(SpecialKeys.Alt, ref structInput);
            DoSpecial(SpecialKeys.Control, ref structInput);
            DoSpecial(SpecialKeys.Shift, ref structInput);
            DoSpecial(SpecialKeys.Windows, ref structInput);

            DoKey(this.key, ref structInput);
            // Key up the actual key-code
            structInput.ki.dwFlags = NativeMethods.KEY_UP;
            DoKey(this.key, ref structInput);

            DoSpecial(SpecialKeys.Alt, ref structInput);
            DoSpecial(SpecialKeys.Control, ref structInput);
            DoSpecial(SpecialKeys.Shift, ref structInput);
            DoSpecial(SpecialKeys.Windows, ref structInput);
        }

        private void DoSpecial(SpecialKeys specialKeys, ref NativeMethods.INPUT structInput)
        {
            if (IsActiveSpecialKey(specialKeys))
            {
                DoKey(SpecialKeysDictionary[specialKeys], ref structInput);
            }
        }

        private void DoSpecial(SpecialKeys specialKeys, ref NativeMethods.INPUT64 structInput)
        {
            if (IsActiveSpecialKey(specialKeys))
            {
                DoKey(SpecialKeysDictionary[specialKeys], ref structInput);
            }
        }

        private bool IsActiveSpecialKey(SpecialKeys key)
        {
            return (this.specialKeys & key) == key;
        }

        private void DoKey(ushort key, ref NativeMethods.INPUT structInput)
        {
            structInput.ki.wVk = key;
            NativeMethods.SendInput(1, ref structInput, Marshal.SizeOf(structInput));
        }

        private void DoKey(ushort key, ref NativeMethods.INPUT64 structInput)
        {
            structInput.ki.wVk = key;
            NativeMethods.SendInput(1, ref structInput, Marshal.SizeOf(structInput));
        }

        /// <summary>
        /// Helper method to get the window handle for the main MCE shell in our session
        /// </summary>
        /// <returns></returns>
        private static IntPtr GetWindowHandle()
        {
            IntPtr hwnd = IntPtr.Zero;
            int mySession;

            using (Process currentProcess = Process.GetCurrentProcess())
                mySession = currentProcess.SessionId;

            foreach (Process p in Process.GetProcessesByName("ehshell"))
            {
                if (p.SessionId == mySession)
                    hwnd = p.MainWindowHandle;
                p.Dispose();
            }
            return hwnd;
        }
    }
}
